package football

import (
	"go.uber.org/atomic"
	global2 "longmen/server/config/global"
	football_behaviour2 "longmen/server/internal/services/nami/api/v5/sports/football/football_behaviour"
	"longmen/server/internal/services/nami/config"
	football2 "longmen/server/internal/services/nami/services/sports/football"
	"longmen/server/internal/services/nami/services/sports/football/fbcache"
	"longmen/server/internal/services/nami/utils"
	"longmen/server/internal/services/nami/utils/ratelimit"
	"longmen/server/internal/services/nami/utils/restyclient"
	"longmen/server/pkg/db/mysql/models/models_sports"
	"sync"
	"time"
)

// Football 内部实现接口
type Football struct{}

// SyncCategory 获取分类列表
// 返回全量分类数据
//
// 数据很少变动，建议请求频次：1天/次
func (f Football) SyncCategory() {
	resp := football_behaviour2.CategoryApiResponse{}
	err := restyclient.GetUrlAndBindRecords(config.FBCategoryUrl+config.BuildParamsAndUserAndSecret(), &resp)
	if err != nil {
		global2.Log.Error("获取足球赛事分类失败: ", err)
		return
	}
	// 调用入库
	football2.CrateCategory(resp.Interface())
}

// SyncCountry 获取国家列表
// 返回全量国家数据
//
// 数据很少变动，建议请求频次：1天/次
func (f Football) SyncCountry() {
	resp := football_behaviour2.CountryApiResponse{}
	err := restyclient.GetUrlAndBindRecords(config.FBCountryUrl+config.BuildParamsAndUserAndSecret(), &resp)
	if err != nil {
		global2.Log.Error("获取足球国家列表失败: ", err)
		return
	}
	// 调用入库
	football2.CrateCountry(resp.Interface())
}

// SyncCompetition 获取赛事列表
// 返回全量赛事数据，可根据时间戳增量获取新增或变动的数据
//
// 1、首次全量更新，根据参数id获取全量数据 (在football_init.go 中进行全量手动初始化)
// 2、后续增量更新，根据参数time增量获取变动数据（建议请求频次：1min/次）
func (f Football) SyncCompetition() {
	resp := football_behaviour2.CompetitionApiResponse{}
	maxId := football2.GetCompetitionId()
	queryStr := config.BuildParamsAndUserAndSecret(map[string]interface{}{
		"id":    maxId,
		"limit": 1000,
	})
	err := restyclient.GetUrlAndBindRecords(config.FBCompetitionUrl+queryStr, &resp)
	if err != nil {
		global2.Log.Error("获取足球赛事列表失败: ", err)
		return
	}
	// 调用入库
	football2.CrateCompetition(resp.Interface())
}

// SyncVenue 获取赛事列表
// 返回全量赛事数据，可根据时间戳增量获取新增或变动的数据
//
// 1、首次全量更新，根据参数id获取全量数据 (在football_init.go 中进行全量手动初始化)
// 2、后续增量更新，根据参数time增量获取变动数据（建议请求频次：1min/次）
func (f Football) SyncVenue() {
	resp := football_behaviour2.VenueApiResponse{}
	startTime := football2.GetVenueTime()
	queryStr := config.BuildParamsAndUserAndSecret(map[string]interface{}{
		"timehelper": startTime,
		"limit":      1000,
	})
	err := restyclient.GetUrlAndBindRecords(config.FBVenue+queryStr, &resp)
	if err != nil {
		global2.Log.Error("获取足球场馆列表失败: ", err)
		return
	}
	// 调用入库
	football2.CrateVenue(resp.Results)
}

// SyncTeam 获取球队列表
// 返回全量球队数据，可根据时间戳增量获取新增或变动的数据
//
// 1、首次全量更新，根据参数id获取全量数据
// 2、后续增量更新，根据参数time增量获取变动数据（建议请求频次：1min/次）
func (f Football) SyncTeam() {
	resp := football_behaviour2.TeamApiResponse{}
	maxId := football2.GetTeamMaxId()
	global2.Log.Debugf("足球球队最大id:%v", maxId)
	queryStr := config.BuildParamsAndUserAndSecret(map[string]interface{}{
		"id":    maxId,
		"limit": 1000,
	})
	err := restyclient.GetUrlAndBindRecords(config.FBTeamsUrl+queryStr, &resp)
	if err != nil {
		global2.Log.Error("获取足球球队列表失败: ", err)
		return
	}
	// 调用入库
	football2.CrateTeam(resp.Interface())
}

// SyncMatch 获取赛程赛果列表-日期查询
// 该接口返回所请求日期的全量赛程赛果数据（请求限制：前后30天）
// 注：实时数据的获取通过实时统计数据接口
//
// 当天赛程 建议请求频次：10min/次（全量更新）
func (f Football) SyncMatch() {
	// 当天赛程
	today := time.Now().Format("20060102")
	syncMatch(today)
}

func syncMatch(today string) {
	resp := football_behaviour2.MatchApiResponse{}
	queryStr := config.BuildParamsAndUserAndSecret(map[string]interface{}{
		"date": today,
	})
	err := restyclient.GetUrlAndBindRecords(config.FBMatchDiaryUrl+queryStr, &resp)
	if err != nil {
		global2.Log.Error("获取足球赛程列表失败: ", err)
		return
	}
	// 调用入库
	football2.CrateMatch(resp.Interface())
}

func (f Football) SyncMatchByDate(date string) {
	syncMatch(date)
}

// SyncMatchByCompetition 获取赛程赛果列表-维度查询
// 该接口返回维度查询对应的全量赛程赛果数据（赛事-最新赛季赛程、体彩-最近72h内彩种赛程）
// 请求次数：120次/min
var syncLastCompId atomic.Int32
var syncMatchByCompetitionLimit, _ = ratelimit.Init(60, 120, 120)

func (f Football) SyncMatchByCompetition() {
	lastId := int(syncLastCompId.Load())
	ids := football2.GetCompetitionIds(lastId)
	if len(ids) == 0 {
		global2.Log.Info("数据已刷新完成")
		syncLastCompId.Store(0)
		return
	}
	wg := sync.WaitGroup{}
	for i, id := range ids {
		if err := syncMatchByCompetitionLimit.Limiting(); err == nil {
			wg.Add(1)
			go func(gid int) {
				defer wg.Done()
				resp := football_behaviour2.MatchParamApiResponse{}
				queryStr := config.BuildParamsAndUserAndSecret(map[string]interface{}{
					"competition_id": gid,
				})
				err := restyclient.GetUrlAndBindRecords(config.FBMatchParamUrl+queryStr, &resp)
				if err != nil {
					global2.Log.Error("获取更新列表失败: ", err)
				}
				football2.CrateMatch(resp.Interface())
			}(id)
			syncLastCompId.Store(int32(ids[i]))
		}
	}
	wg.Wait()
}

// SyncFutureMatch 获取赛程赛果列表-日期查询
// 该接口返回所请求日期的全量赛程赛果数据（请求限制：前后30天）
// 注：实时数据的获取通过实时统计数据接口
//
// 未来赛程 建议请求频次：30min/次（全量更新）
func (f Football) SyncFutureMatch() {
	for i := 1; i <= 7; i++ {
		formatDay := time.Now().AddDate(0, 0, i).Format("20060102")
		syncMatch(formatDay)
	}
}

func (f Football) SyncLive() {
}

// SyncDelete 获取删除数据
// 返回最近24小时内删除的数据id（比赛、球队、球员、赛事、赛季、阶段），需定时同步
//
// 建议请求频次：1~5min/次
func (f Football) SyncDelete() {
	resp := football_behaviour2.DeleteApiResponse{}
	queryStr := config.BuildParamsAndUserAndSecret(map[string]interface{}{})
	err := restyclient.GetUrlAndBindRecords(config.FBDelete+queryStr, &resp)
	if err != nil {
		global2.Log.Error("获取删除列表失败: ", err)
		return
	}
	// 调用入库
	if len(resp.Results.Team) > 0 {
		football2.DeleteTeams(resp.Results.Team)
	}

	if len(resp.Results.Match) > 0 {
		football2.DeleteMatch(resp.Results.Match)
		global2.Sport().Where("match_id in ?", resp.Results.Match).Delete(models_sports.MatchFavorite{})
		global2.Sport().Where("match_id in ?", resp.Results.Match).Delete(models_sports.MatchFavoriteStatistics{})
	}

	if len(resp.Results.Competition) > 0 {
		football2.DeleteCompetition(resp.Results.Competition)
	}
}

var footballRecentId int

// SyncRecent 获取变动比赛列表
// 用于获取比赛变动后的数据（比赛时间、状态等），可根据时间戳增量获取新增或变动的数据
// 限制：前30天比赛开始
//
// 1、首次全量更新，根据参数id获取全量数据
// 2、后续增量更新，根据参数time增量获取变动数据（建议请求频次：1min/次）
func (f Football) SyncRecent() {
	resp := football_behaviour2.RecentApiResponse{}
	queryStr := config.BuildParamsAndUserAndSecret(map[string]interface{}{
		"id":    footballRecentId,
		"limit": 1000,
	})
	err := restyclient.GetUrlAndBindRecords(config.FBRecentChange+queryStr, &resp)
	if err != nil {
		global2.Log.Error("获取更新列表失败: ", err)
		return
	}
	football2.UpdateMatchAllField(resp.UpdateMatchInterFaces())
	if resp.Query.Total != 1000 {
		footballRecentId = 0
	} else {
		footballRecentId = resp.Query.MaxID
	}
}

// SyncLineupWork 获取比赛阵容详情
//返回单场比赛阵容数据（根据“赛程赛果接口”中“是否有阵容”字段来判断是否调用该接口）
//请求次数：120次/min
//
//坐标说明：
//主队坐标原点：左上；即：x轴方向向右，y轴方向向下；
//客队坐标原点：右下；即：x轴方向向左，y轴方向向上。
//
//含球员事件，请求限制：前30天比赛

var fbLineupLastId atomic.Int32

func (f Football) SyncLineupWork() {
	startTime, endTime := utils.GetDayStart2End()
	lastId := int(fbLineupLastId.Load())
	ids := football2.GetLiveUpMatchIds(startTime, endTime, lastId)
	if len(ids) == 0 {
		global2.Log.Info("数据已刷新完成")
		fbLineupLastId.Store(0)
		return
	}
	for i, id := range ids {
		resp := football_behaviour2.LineUpApiResponse{}
		queryStr := config.BuildParamsAndUserAndSecret(map[string]interface{}{
			"id": id,
		})
		err := restyclient.GetUrlAndBindRecords(config.FBLineUp+queryStr, &resp)
		if err != nil {
			global2.Log.Error("获取更新阵容列表失败: ", err)
			continue
		}
		if resp.Code != 0 {
			global2.Log.Error("获取更新阵容列表失败: ", err)
			continue
		}
		resp.Results.MatchId = id
		// 一次只推送一场
		err = fbcache.NewLineUpRedis().PublishLineup(resp.Results)
		if err != nil {
			global2.Log.Error("发送阵容数据到nats失败: ", err)
		}
		fbLineupLastId.Store(int32(ids[i]))
	}

}
